/***************************************************************************
 *   oboe.utils.Prototype                                                  *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.utils
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;
	
	import oboe.core.Controller;
	import oboe.core.Oboe;

	public final class Prototype
	{
		private static var hasOboeExtensions:Boolean = false;
		
		public static function addMethod( c:Class, name:String, func:Function ):void
		{
			c.prototype[name] = func;
			c.prototype.setPropertyIsEnumerable( name, false );
		}
		
		public static function addOboeExtensions():void
		{
			if( hasOboeExtensions ) return;
			
			addMethod( DisplayObject, "getController", function ():Controller {
				return Oboe.controllerLookup[this];
			});
			
			addMethod( Object, "toDictionary", function ( weakKeys : Boolean=false ) : Dictionary {
				var ret : Dictionary = new Dictionary( weakKeys );
				for( var i : Object in this ){
					ret[i] = this[i];
				}
				return ret;
			});
			
			addMethod( Object, "$", function():String{ return "X"; });
			
			addMethod( URLLoader, "loadEx", function( url:URLRequest ):void{
				this.addEventListener( IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void{ trace( "Error downloading: " + url.url ); } );
				this.load( url );
			});
			
			addMethod( Loader, "loadEx", function( url:URLRequest ):void{
				this.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void{ trace( "Error downloading: " + url.url ); } );
				this.load( url );
			});
			
			addMethod( EventDispatcher, "removeCallback", function( e:CallbackHandle ):void
			{
				this.removeEventListener( e.event, e.cb );
			});
			
			addMethod( EventDispatcher, "addCallback", function( e:String, f:Function, ... args ):CallbackHandle
			{
				var r:CallbackHandle = new CallbackHandle(e);
				
				var target:EventDispatcher = this;
				
				var cb:Function = function (ev:Event):void 
				{
					if( r.ignoreCount )
					{
						r.ignoreCount--;
						return;
					}
					if( r.once )
					{
						Prototype.removeCallback( target, r );
					}
					f.apply( null, args );
				};

				this.addEventListener( e, cb );

				r.cb = cb;
				
				return r;
			});

			addMethod( EventDispatcher, "addCallbackEx", function( e:String, f:Function, ... args ):CallbackHandle
			{
				var r:CallbackHandle = new CallbackHandle(e);

				var target:EventDispatcher = this;
				
				var cb:Function = function (ev:Event):void 
				{
					if( r.ignoreCount )
					{
						r.ignoreCount--;
						return;
					}
					if( r.once )
					{
						Prototype.removeCallback( target, r );
					}
					args.unshift( ev );
					f.apply( null, args );
					args.shift();
				};
				
				this.addEventListener( e, cb );
				
				r.cb = cb;
				
				return r;
			});
			
			hasOboeExtensions = true;
		}
		
		public static function getController( target:DisplayObject ):Controller {
			return Oboe.controllerLookup[ target ];
		}
		
		public static function addCallback( target:EventDispatcher, e:String, f:Function, ... args ):CallbackHandle
		{
			var r:CallbackHandle = new CallbackHandle(e);

			var cb:Function = function (ev:Event):void 
			{
				if( r.ignoreCount )
				{
					r.ignoreCount--;
					return;
				}
				if( r.once )
				{
					Prototype.removeCallback( target, r );
				}
				f.apply( null, args );
			};
			
			target.addEventListener( e, cb );
			
			r.cb = cb;
			
			return r;
		}

		public static function addCallbackEx( target:EventDispatcher, e:String, f:Function, ... args ):CallbackHandle
		{
			var r:CallbackHandle = new CallbackHandle(e);
			
			var cb:Function = function (ev:Event):void 
			{
				if( r.ignoreCount )
				{
					r.ignoreCount--;
					return;
				}
				if( r.once )
				{
					Prototype.removeCallback( target, r );
				}
				args.unshift( ev );
				f.apply( null, args );
				args.shift();				
			};
			
			target.addEventListener( e, cb );
			r.cb = cb;
			
			return r;
		}
		
		public static function removeCallback( target:EventDispatcher, e:CallbackHandle ):void
		{
			target.removeEventListener( e.event, e.cb );
		}
	}
}